package com.aiwiown.face.internal.util;

import com.aiwiown.face.ApiConstants;
import com.aiwiown.face.ApiMethod;

import javax.net.ssl.SSLException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

/**
 * @ClassName : WebUtils
 * @Description : 网络工具类
 * @Author : dbin0123
 * @Date: 2020-05-06 16:52
 */
public class WebUtils {

    private static String boundaryString = getBoundary();

    /**
     * request请求
     *
     * @param apiUrl
     * @param apiMethod
     * @param paramMap
     * @param fileMap
     * @return
     * @throws Exception
     */
    public static byte[] request(String apiUrl, ApiMethod apiMethod, Map<String, String> paramMap, Map<String, byte[]> fileMap, Integer connectTimeout, Integer readTimeout, String charset) throws Exception {
        byte[] res = null;
        switch (apiMethod) {
            case GET:
                res = WebUtils.get(apiUrl, paramMap, connectTimeout, readTimeout, charset);
                break;
            case POST:
                res = WebUtils.post(apiUrl, paramMap, fileMap, connectTimeout, readTimeout, charset);
                break;
            default:
                return new byte[0];
        }
        return res;
    }

    /**
     * get请求
     *
     * @param apiUrl
     * @param paramMap
     * @param connectTimeout
     * @param readTimeout
     * @param charset
     * @return
     */
    private static byte[] get(String apiUrl, Map<String, String> paramMap, Integer connectTimeout, Integer readTimeout, String charset) throws IOException {
        HttpURLConnection conn = null;
        try {
            try {
                conn = (HttpURLConnection) new URL(buildQuery(apiUrl, paramMap, charset)).openConnection();
                conn.setConnectTimeout(getIntDefault(connectTimeout, ApiConstants.CONNECT_TIME_OUT));
                conn.setReadTimeout(getIntDefault(readTimeout, ApiConstants.READ_OUT_TIME));
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setUseCaches(false);
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)");
            } catch (IOException e) {
                ApiLogger.logCommError(e, apiUrl, paramMap.get("app_key"), ApiMethod.GET.name(), paramMap);
                throw e;
            }
            InputStream inputStream = null;
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int code = conn.getResponseCode();
                try {
                    if (code == 200) {
                        inputStream = conn.getInputStream();
                    } else {
                        inputStream = conn.getErrorStream();
                    }
                } catch (SSLException e) {
                    e.printStackTrace();
                    return new byte[0];
                }
                byte[] buff = new byte[4096];
                int len;
                while ((len = inputStream.read(buff)) != -1) {
                    baos.write(buff, 0, len);
                }
                return baos.toByteArray();
            } catch (IOException e) {
                ApiLogger.logCommError(e, apiUrl, paramMap.get("app_key"), ApiMethod.GET.name(), paramMap);
                throw e;
            } finally {
                if (null != inputStream) {
                    inputStream.close();
                }
            }
        } finally {
            if (conn != null) {
                conn.disconnect();

            }
        }
    }

    /**
     * post请求
     *
     * @param url
     * @param paramMap
     * @param fileMap
     * @return
     */
    private static byte[] post(String url, Map<String, String> paramMap, Map<String, byte[]> fileMap, Integer connectTimeout, Integer readTimeout, String charset) throws IOException {
        HttpURLConnection conn = null;
        OutputStream out = null;
        try {
            try {
                conn = (HttpURLConnection) new URL(url).openConnection();
                conn.setConnectTimeout(getIntDefault(connectTimeout, ApiConstants.CONNECT_TIME_OUT));
                conn.setReadTimeout(getIntDefault(readTimeout, ApiConstants.READ_OUT_TIME));
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setUseCaches(false);
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)");
            } catch (IOException e) {
                Map<String, String> map = getParamsFromUrl(url);
                ApiLogger.logCommError(e, url, map.get("app_key"), ApiMethod.POST.name(), map);
                throw e;
            }
            try (OutputStream outputStream = conn.getOutputStream();
                 DataOutputStream obos = new DataOutputStream(outputStream)) {
                Iterator iter = paramMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry<String, String> entry = (Map.Entry) iter.next();
                    String key = entry.getKey();
                    String value = entry.getValue();
                    obos.writeBytes("--" + boundaryString + "\r\n");
                    obos.writeBytes("Content-Disposition: form-data; name=\"" + key
                            + "\"\r\n");
                    obos.writeBytes("\r\n");

                    if (StringUtils.isContainChinese(value)) {
                        obos.write(value.getBytes());
                        obos.writeBytes("\r\n");
                    } else {
                        obos.writeBytes(value + "\r\n");
                    }
                }
                if (fileMap != null && fileMap.size() > 0) {
                    Iterator fileIter = fileMap.entrySet().iterator();
                    while (fileIter.hasNext()) {
                        Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIter.next();
                        obos.writeBytes("--" + boundaryString + "\r\n");
                        obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey() + "\"; filename=\"" + encode(" ", charset) + "\"\r\n");
                        obos.writeBytes("\r\n");
                        obos.write(fileEntry.getValue());
                        obos.writeBytes("\r\n");
                    }
                }
                obos.writeBytes("--" + boundaryString + "--" + "\r\n");
                obos.writeBytes("\r\n");
                obos.flush();
            } catch (IOException e) {
                Map<String, String> map = getParamsFromUrl(url);
                ApiLogger.logCommError(e, conn, map.get("app_key"), ApiMethod.POST.name(), map);
                throw e;
            }
            InputStream inputStream = null;
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int code = conn.getResponseCode();
                try {
                    if (code == 200) {
                        inputStream = conn.getInputStream();
                    } else {
                        inputStream = conn.getErrorStream();
                    }
                } catch (SSLException e) {
                    e.printStackTrace();
                    return new byte[0];
                }
                byte[] buff = new byte[4096];
                int len;
                while ((len = inputStream.read(buff)) != -1) {
                    baos.write(buff, 0, len);
                }
                return baos.toByteArray();
            } catch (IOException e) {
                Map<String, String> map = getParamsFromUrl(url);
                ApiLogger.logCommError(e, conn, map.get("app_key"), ApiMethod.POST.name(), map);
                throw e;
            } finally {
                if (null != inputStream) {
                    inputStream.close();
                }
            }
        } finally {
            if (out != null) {
                out.close();
            }
            if (conn != null) {
                conn.disconnect();

            }
        }
    }

    /**
     * 编码使用UTF-8
     *
     * @param value
     * @return
     * @throws Exception
     */
    private static String encode(String value, String enc) throws IOException {
        return URLEncoder.encode(value, enc);
    }

    /**
     * 从url解析参数
     *
     * @param url
     * @return
     */
    private static Map<String, String> getParamsFromUrl(String url) {
        Map<String, String> map = null;
        if (url != null && url.indexOf('?') != -1) {
            map = splitUrlQuery(url.substring(url.indexOf('?') + 1));
        }
        if (map == null) {
            map = new HashMap<String, String>();
        }
        return map;
    }

    /**
     * 从URL中提取所有的参数。
     *
     * @param query URL地址
     * @return 参数映射
     */
    public static Map<String, String> splitUrlQuery(String query) {
        Map<String, String> result = new HashMap<String, String>();

        String[] pairs = query.split("&");
        if (pairs != null && pairs.length > 0) {
            for (String pair : pairs) {
                String[] param = pair.split("=", 2);
                if (param != null && param.length == 2) {
                    result.put(param[0], param[1]);
                }
            }
        }

        return result;
    }

    /**
     * @param api
     * @param urlParams
     * @param charset
     * @return
     * @throws IOException
     */
    public static String buildQuery(String api, Map<String, String> urlParams, String charset) throws IOException {
        if (StringUtils.isEmpty(api)) {
            return null;
        }
        if (urlParams == null || urlParams.isEmpty()) {
            return api;
        }
        StringBuilder sb = new StringBuilder(api);
        if (api.contains("?")) {
            if (!api.endsWith("?")) {
                if (api.lastIndexOf("=") < api.lastIndexOf("?") + 2) {
                    return api;
                }
                if (!api.endsWith("&")) {
                    sb.append('&');
                }
            }
        } else {
            sb.append('?');
        }
        for (String name : urlParams.keySet()) {
            sb.append(name).append('=').append(URLEncoder.encode(urlParams.get(name), charset)).append('&');
        }
        sb.delete(sb.length() - 1, sb.length());
        return sb.toString();
    }

    /**
     * @param int1
     * @param int2
     * @return
     */
    private static int getIntDefault(Integer int1, Integer int2) {
        if (null == int1 || int1.compareTo(Integer.valueOf("0")) == 0) {
            return int2;
        }
        return int1;
    }

    /**
     * 获取边界
     *
     * @return
     */
    private static String getBoundary() {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 32; ++i) {
            sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length())));
        }
        return sb.toString();
    }
}
